vimfandomcom-20200223-history
Move cursor by display lines when wrapping
The command :set wrap lbr will wrap long lines between words. However, when you move the cursor down (or up), the cursor will jump from one physical line to the next. You can press j''' to move down one physical line, or '''gj to move down one displayed line. To make it easy, you could put the following in your vimrc file. Then press \w to toggle wrapping on or off (that's a backslash then w, assuming the default Leader key). When wrap is on, the cursor movement keys are mapped to move by display lines. noremap w :call ToggleWrap() function ToggleWrap() if &wrap echo "Wrap OFF" setlocal nowrap set virtualedit=all silent! nunmap silent! nunmap silent! nunmap silent! nunmap silent! iunmap silent! iunmap silent! iunmap silent! iunmap else echo "Wrap ON" setlocal wrap linebreak nolist set virtualedit= setlocal display+=lastline noremap gk noremap gj noremap g noremap g inoremap gk inoremap gj inoremap g inoremap g endif endfunction Comments You can add the following to get the `standard keys' to work: noremap k gk noremap j gj noremap 0 g0 noremap $ g$ ---- If you copy the code from the tip into your vimrc file, make sure that you delete trailing spaces from each line, AND that you put the new code near the bottom of the vimrc file. If the code is near the top of vimrc, something coming after it might redefine those keys. ---- Unfortunately, this tip breaks the movement commands in conjunction with other commands. For example, d will no longer delete two lines but delete from the cursor to the position in the next line. :But you can still use j to move down by a physical line, and dj will do what you want. : : You can avoid this by mapping the keys in the operator-pending mode as well: onoremap j gj onoremap k gk Additional care is required to not break Omnicompletion when remapping arrow keys. I have yet to find where this breaks: function! NoremapNormalCmd(key, preserve_omni, ...) let cmd = '' let icmd = '' for x in a:000 let cmd .= x let icmd .= "" . x endfor execute ":nnoremap " . a:key . " " . cmd execute ":vnoremap " . a:key . " " . cmd if a:preserve_omni execute ":inoremap " . a:key . " pumvisible() ? \"" . a:key . "\" : \"" . icmd . "\"" else execute ":inoremap " . a:key . " " . icmd endif endfunction " Cursor moves by screen lines call NoremapNormalCmd("", 1, "gk") call NoremapNormalCmd("", 1, "gj") call NoremapNormalCmd("", 0, "g") call NoremapNormalCmd("", 0, "g") " PageUp/PageDown preserve relative cursor position call NoremapNormalCmd("", 0, "", "") call NoremapNormalCmd("", 0, "", "") ---- TODO: On Windows, if you use the above to wrap, you can then press Shift+Down or Shift+Up to perform a selection. However, after releasing the Shift key, moving the cursor up/down does not exit from selection mode. Can that be fixed? ---- TODO: Following is the original tip. Is there anything below which needs to be kept, or should it all be deleted? If you're tired of the cursor jumping past 5 lines when :set wrap then add these mappings to your vimrc file. nnoremap j gj nnoremap k gk vnoremap j gj vnoremap k gk nnoremap gj nnoremap gk vnoremap gj vnoremap gk inoremap gj inoremap gk What they do is remap the cursor keys to use there `g' equvilant. See I added support for visual mode while in wrap mode. To remove this, you can just remove any line starting with 'vnoremap' or 'vunmap' ---- To enable/disable the key mappings with wrap: nnoremap c :call ChooseWrap() function ChooseWrap() let l:choice=confirm("Toggle Wrapping?", "&yes\n&no", 0) if l:choice 1 if &wrap call DisableDisplayWrapping() else call EnableDisplayWrapping() endif endif endfunction function EnableDisplayWrapping() if !&wrap setlocal wrap " don't skip wrapped lines nnoremap gk nnoremap gj inoremap gk inoremap gj vnoremap gk vnoremap gj endif endfunction function DisableDisplayWrapping() if &wrap setlocal nowrap nunmap nunmap iunmap iunmap vunmap vunmap endif endfunction (if you hit 'c' in command mode, it'll pop up a menu asking if you want to toggle wrapping - you could obviously change this slightly to have it ask you if you would like to enable or disable wrapping) ---- I found the following to work pretty nicely. It maps movements per-screen (using the 'g' prefix) only when in wrap mode. I added support while in wrap is set for visual selections. If you want to remove this, just remove any of the lines below that begin with the word 'vnoremap'. Put the following in your vimrc: " mapping to make movements operate on 1 screen line in wrap mode function! ScreenMovement(movement) if &wrap && b:gmove 'yes' return "g" . a:movement else return a:movement endif endfunction onoremap j ScreenMovement("j") onoremap k ScreenMovement("k") onoremap 0 ScreenMovement("0") onoremap ^ ScreenMovement("^") onoremap $ ScreenMovement("$") nnoremap j ScreenMovement("j") nnoremap k ScreenMovement("k") nnoremap 0 ScreenMovement("0") nnoremap ^ ScreenMovement("^") nnoremap $ ScreenMovement("$") vnoremap j ScreenMovement("j") vnoremap k ScreenMovement("k") vnoremap 0 ScreenMovement("0") vnoremap ^ ScreenMovement("^") vnoremap $ ScreenMovement("$") vnoremap j ScreenMovement("j") " toggle showbreak function! TYShowBreak() if &showbreak '' set showbreak=> else set showbreak= endif endfunction let b:gmove = "yes" function! TYToggleBreakMove() if exists("b:gmove") && b:gmove "yes" let b:gmove = "no" else let b:gmove = "yes" endif endfunction nmap ,b TYShowBreak() nmap ,bb TYToggleBreakMove() ---- Also to select displayed lines using shift + home/end/arrow-keys: noremap vgoo noremap vg noremap vgk noremap vgj inoremap vgoo inoremap vg inoremap vgk inoremap vgj ----